﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using XNAGuiLib.GuiLibKeyboard;

namespace XNAGuiLib
{
    public class KeyProcessor : IKeyInvokable, IScrollable
    {
        #region Field - Font
        private SpriteFont _font;
        /// <summary>
        /// gets or sets the Font
        /// </summary>
        public SpriteFont Font
        {
            get
            {
                return _font;
            }
            set
            {
                _font = value;
            }
        }
        #endregion

        #region Field - LineWidth
        private int _lineWidth = 0;
        /// <summary>
        /// gets or sets the single line width for the Text
        /// </summary>
        public int LineWidth
        {
            get
            {
                return _lineWidth;
            }
            set
            {
                _lineWidth = value;
            }
        }
        #endregion

        #region Field - TotalLines
        private int _totalLines = 0;
        /// <summary>
        /// gets or sets the amount of lines to be managed by the key processor
        /// </summary>
        public int TotalLines
        {
            get
            {
                return _totalLines;
            }
            set
            {
                _totalLines = value;
                _totalContent = new string[value];
            }
        }
        #endregion


        // Input related fields
        #region Field - Content
        private string _content = "";
        private string _oldContent = "";
        /// <summary>
        /// gets or sets the Content
        /// </summary>
        public String Content
        {
            get
            {
                return _content;
            }
            set
            {
                _content = value;
            }
        }
        #endregion

        #region Field - Textmark
        private int _textmark = 0;
        /// <summary>
        /// gets or sets the Textmark
        /// </summary>
        public int Textmark
        {
            get
            {
                return _textmark;
            }
            set
            {
                _textmark = value;
            }
        }
        #endregion

        #region Field - LogLine
        private int _logLine = 0;
        /// <summary>
        /// gets or sets the current logLine the Textmark is in
        /// </summary>
        public int LogLine
        {
            get
            {
                return _logLine;
            }
            set
            {
                _logLine = value;
            }
        }
        #endregion


        #region Field List - Log
        private List<string> _log = new List<string>();
        /// <summary>
        /// gets the complete _log
        /// </summary>
        public List<string> Log
        {
            get
            {
                return _log;
            }
        }
        #endregion

        #region Field List - TotalContent
        private string[] _totalContent;
        /// <summary>
        /// gets the complete _totalContent
        /// </summary>
        public string[] TotalContent
        {
            get
            {
                return _totalContent;
            }
        }
        #endregion

        // Region containing the "Pressed" counter for special keys to get them captured when key is kept pressed down
        #region Private Fields - (xx)Pressed
        private byte _backPressed = 0;
        private byte _leftPressed = 0;
        private byte _rightPressed = 0;
        private byte _upPressed = 0;
        private byte _downPressed = 0;
        #endregion

        #region Private Field - _currKey
        private KeyboardState _currKey;
        #endregion

        #region Private Field - _prevKey
        private KeyboardState _prevKey;
        #endregion


        #region Constructor - KeyProcessor()
        public KeyProcessor()
        {
            _log.Add(_content);
            _totalLines = 1;
            _totalContent = new string[_totalLines];
        }
        #endregion

        #region Constructor - KeyProcessor(int totalLines)
        public KeyProcessor(int totalLines)
        {
            _log.Add(_content);
            _totalLines = totalLines;
            _totalContent = new string[_totalLines];
        }
        #endregion


        #region Method - CheckInput()
        public void CheckInput(Keyboards.KeyboardLayout layout, IScrollbar iScrollbar)
        {
            int logLine = _logLine;

            _oldContent = _content;

            _prevKey = _currKey;
            _currKey = Keyboard.GetState();

            char[][] dict;

            // Check the KeyProcessor setting for the dictionary to use for this input
            #region KeyProcessorCheck - Dictionary to use
            if (layout == Keyboards.KeyboardLayout.EU_GE)
                dict = Keyboards.Dictionary_Europe_German;
            else if (layout == Keyboards.KeyboardLayout.US_NA)
                dict = Keyboards.Dictionary_UnitedStates_NorthAmerica;
            else dict = Keyboards.Dictionary_Europe_German;
            #endregion

            // Check input for dictionary letters
            #region InputCheck - Dictionary Letters
            for (int i = 0; i < Keyboards.KeyMap.Length; i++)
            {

                if (_currKey.IsKeyDown(Keyboards.KeyMap[i]) && _prevKey.IsKeyUp(Keyboards.KeyMap[i]))
                {
                    string letter = dict[i][1].ToString();

                    if (_currKey.IsKeyDown(Keys.LeftShift) || _currKey.IsKeyDown(Keys.RightShift))
                        letter = dict[i][0].ToString();

                    if (_currKey.IsKeyDown(Keys.LeftAlt) || _currKey.IsKeyDown(Keys.RightAlt))
                        letter = dict[i][2].ToString();

                    _content = _content.Insert(_textmark, letter);
                    _textmark++;                    
                }
            }
            #endregion
            // Check input for backspace
            #region InputCheck - Back
            if (_currKey.IsKeyDown(Keys.Back))
            {
                // if _backPressed reached 3 start the processing with the "back"-Key
                if (_backPressed >= 4)
                {
                    // if _textmark is greater zero and content have any value
                    if (_content != "" && _textmark > 0)
                    {
                        // decrease _textmark by 1 for the correct position in _content
                        _textmark--;
                        // _content = _content.Remove(_content.Length - 1, 1);
                        _content = _content.Remove(_textmark, 1);
                    }

                    // if _textmark is smaller or equals to zero
                    else if (_textmark <= 0)
                    {
                        // if _content have any value
                        if (_content != "")
                        {
                            // Console.WriteLine("Da steht noch was in der Zeile!");
                            // =====================================================
                            // if _logLine is smaller or equals to count of _log's members and _logLine is greater zero
                            if (_logLine <= _log.Count && _logLine > 0)
                            {
                                //
                                if (_font.MeasureString(_log[_logLine - 1] + _log[_logLine]).X <= LineWidth)
                                {
                                    // Console.WriteLine("In der vorigen Zeile ist genug noch platz für die aktuelle Zeile!");
                                    // =======================================================================================
                                    // set _textmark to the end of the previous line
                                    _textmark = _log[_logLine - 1].Length;
                                    // add current line to the end of the previous line
                                    _log[_logLine - 1] += _content;
                                    // remove list-entry for the current line
                                    _log.RemoveAt(_logLine);
                                    // decrease _logLine (line-counter) to the previous line/make previous to current line
                                    _logLine--;
                                    // set _content to the now current line (previous line before)
                                    _content = _log[_logLine];
                                }
                            }
                        }
                        else
                        {
                            // if _logLine is smaller or equals to count of _log's member and _logLine is greater zero
                            if (_logLine <= _log.Count && _logLine > 0)
                            {
                                // remove list-entry for the current line
                                _log.RemoveAt(_logLine);
                                // decrease _logLine (line-counter) to the previous line/make previous to current line
                                _logLine--;
                                // set _content to the now current line (previous line before)
                                _content = _log[_logLine];
                                // set _textmark to the end of the current line
                                _textmark = _content.Length;
                            }
                        }
                    }
                    // set the _backPressed counter back to zero (start new cycle)
                    _backPressed = 0;
                }
                else _backPressed++;
            }
            #endregion
            // Check input for enter
            #region InputCheck - Enter
            if (_currKey.IsKeyDown(Keys.Enter) && _prevKey.IsKeyUp(Keys.Enter)
                && !CheckAltKeys(_currKey.GetPressedKeys()))
            {
                _log[_logLine] = _content.Substring(0, _textmark);
                _logLine++;
                if (_logLine >= _log.Count)
                {
                    _log.Add(_content.Substring(_textmark));
                    _content = _log[_logLine];
                }
                else
                {
                    _log.Insert(_logLine, _content.Substring(_textmark));
                    _content = _log[_logLine];
                }
                _textmark = 0;
            }
            #endregion
            // Check input for up
            #region InputCheck - Up
            if (_currKey.IsKeyDown(Keys.Up) && _logLine > 0)
            {
                if (_upPressed == 4)
                {
                    InvokeUp();
                    _upPressed = 0;
                }
                else _upPressed++;
            }
            #endregion
            // Check input for down
            #region InputCheck - Down
            if (_currKey.IsKeyDown(Keys.Down) && _logLine < _log.Count)
            {
                if (_downPressed == 4)
                {
                    InvokeDown();
                    _downPressed = 0;
                }
                else _downPressed++;
            }
            #endregion
            // Check input for left
            #region InputCheck - Left
            if (_currKey.IsKeyDown(Keys.Left))
            {
                if (_leftPressed == 3)
                {
                    if (_textmark == 0)
                    {
                        if (_logLine > 0)
                        {
                            _log[_logLine] = _content;
                            _logLine--;
                            _content = _log[_logLine];
                            _textmark = _content.Length;
                        }
                    }
                    else
                    {
                        _textmark--;
                    }
                    _leftPressed = 0;
                }
                else _leftPressed++;
            }
            #endregion
            // Check input for right
            #region InputCheck - Right
            if (_currKey.IsKeyDown(Keys.Right))
            {
                if (_rightPressed == 3)
                {
                    if (_textmark == _content.Length)
                    {
                        if (_logLine < _log.Count - 1)
                        {
                            _log[_logLine] = _content;
                            _logLine++;
                            _content = _log[_logLine];
                            _textmark = 0;
                        }
                    }
                    else
                    {
                        _textmark++;
                    }
                    _rightPressed = 0;
                }
                else _rightPressed++;
            }
            #endregion

            _log[LogLine] = _content;

            if (_logLine >= 0)
            {
                iScrollbar.ScrollSteps(_log.Count);
                iScrollbar.BlockOnStep(_logLine);
            }
        }
        #endregion


        #region Method - TrimBehindTextmark()
        public void TrimBehindTextmark()
        {
            this._log[LogLine] = this._log[LogLine].Substring(0, _textmark);
            this._content = this._content.Substring(0, _textmark);
        }
        #endregion

        #region Method - CheckPaste()
        public static bool CheckPaste()
        {
            KeyboardState ks = Keyboard.GetState();
            if (CheckCtrlKeys(ks.GetPressedKeys()) && ks.IsKeyDown(Keys.V))
            {
                return true;
            }
            else return false;
        }
        #endregion


        #region Private Method - InvokeUp()
        private void InvokeUp()
        {
            if (_log.Count > 0 && _logLine <= _log.Count && _logLine > 0)
            {
                _log[_logLine] = _content;
                _logLine--;
                _content = _log[_logLine];

                if (_textmark > _content.Length)
                    _textmark = _content.Length;
            }
        }
        #endregion

        #region Private Method - InvokeDown()
        private void InvokeDown()
        {
            if (_log.Count > 0 && _logLine < _log.Count - 1 )
            {
                _log[_logLine] = _content;
                _logLine++;
                _content = _log[_logLine];

                if (_textmark > _content.Length)
                    _textmark = _content.Length;
            }
        }
        #endregion


        #region Private Method - CheckCtrlKeys(Keys[] keys)
        private static bool CheckCtrlKeys(Keys[] keys)
        {
            foreach (Keys k in keys)
            {
                if (k == Keys.RightControl || k == Keys.LeftControl)
                    return true;
            }

            return false;
        }
        #endregion

        #region Private Method - CheckShiftKeys(Keys[] keys)
        private static bool CheckShiftKeys(Keys[] keys)
        {
            foreach (Keys k in keys)
            {
                if (k == Keys.RightShift || k == Keys.LeftShift) 
                    return true;
            }

            return false;
        }
        #endregion

        #region Private Method - CheckAltKeys(Keys[] keys)
        private static bool CheckAltKeys(Keys[] keys)
        {
            foreach (Keys k in keys)
            {
                if (k == Keys.RightAlt || k == Keys.LeftAlt)
                    return true;
            }

            return false;
        }
        #endregion


        #region Override Method - Invoke(Keys key)
        public void Invoke(Keys key)
        {
            if (key == Keys.Up)
            {
                InvokeUp();
            }
            else if (key == Keys.Down)
            {
                InvokeDown();
            }

            Console.WriteLine(_logLine);
        }
        #endregion



        #region Interface - IScroll Members
        public void Scroll(IScrollClass.ScrollDirection direction)
        {
            DoScroll(direction);
        }
        #endregion
       
        #region Override Method - DoScroll(IScrollClass.ScrollDirection direction)
        public void DoScroll(IScrollClass.ScrollDirection direction)
        {
            if (direction == IScrollClass.ScrollDirection.Up)
            {
                this.Invoke(Keys.Up);
                Console.WriteLine("Do Scroll in direction" + direction.ToString());
            }
            else if (direction == IScrollClass.ScrollDirection.Down)
            {
                this.Invoke(Keys.Down);
                Console.WriteLine("Do Scroll in direction" + direction.ToString());
            }
        }
        #endregion
    }
}